package gazzera.node.core.plugins.shareplugin;

import gazzera.gui.*;
import gazzera.gui.messages.*;
import gazzera.node.core.*;
import gazzera.node.core.messages.*;
import gazzera.node.core.peer.*;
import gazzera.node.core.plugins.shareplugin.db.*;
import gazzera.utils.*;

import java.io.*;
import java.util.*;

public class SharePlugin implements Plugin
{
    private ICore core = null;
    private ShareList shares = null;
    private HashDatabase db = null;
    
	@Override
	public void command(GuiMessage msg) throws Exception
	{
		if ( msg instanceof GuiStartBrowseSearch)
		{
			String hash = ((GuiStartBrowseSearch)msg).hash;
			String parent = ((GuiStartBrowseSearch)msg).parent;
			
			Node n = core.find(hash);
			if(n!=null)
			{
				n.enqueue(new BrowseSearch(parent));
			}
		}
		else if(msg instanceof Status)
		{
			notifyShares();
		}
		else if (msg instanceof RefreshShares)
		{
			RefreshShares es = (RefreshShares) msg;
			refreshShare(es);
		}
		else if (msg instanceof EditShare)
		{
			EditShare es = (EditShare) msg;
			editShare(es);
		}
		else if (msg instanceof RemoveShare)
		{
			RemoveShare es = (RemoveShare) msg;
			removeShare(es);
		}
	}

	
	private void removeShare(RemoveShare msg) throws IOException
	{
		core.getConfig().set("share."+msg.shareId+".folder", "");
		core.getConfig().set("share."+msg.shareId+".visibility", "");
		core.getConfig().save();
		
		core.getGui().pass(new ShareInfo(msg.shareId, "", new ArrayList<String>()));
		shares = calcShareList(); // ricalculate shared info
	}
	

	private void refreshShare(RefreshShares msg) throws IOException
	{
		db.cleanEnqueued();
		shares = calcShareList();
	}
	
	private void editShare(EditShare msg) throws IOException
	{
		Config c = core.getConfig();
		int id = msg.shareId;
		if (id == -1)
		{
			// new share!
			id = getNextId(c, id);
		}
		
		String groups = Str.join(msg.groups, ",").trim();
		
		c.set("share."+id+".folder", msg.folder);
		c.set("share."+id+".visibility", groups);
		
		c.save();
		shares = calcShareList(); // ricalculate shared info
		core.getGui().pass(new ShareInfo(id, msg.folder, msg.groups));
	}

	private int getNextId(Config c, int id)
	{
		int i = 1;
		while(true)
		{
			String share = c.get("share."+i+".folder");
			if (share == null)
			{
				id = i;
			    break;
			}
			i++;
		}
		return id;
	}
	

	@Override
	public void dataPumpTerminated(Node node, DataPumpSource src) throws Exception
	{
	
	}

	@Override
	public void finish() throws Exception
	{
	    db.close();
	}

	@Override
	public void init(ICore core) throws Exception
	{
	    this.core = core;
	    db = new HashDatabase(core.getEnvironment());
	    shares = calcShareList();
	}
	
	
	public ShareList getShares()
	{
		return shares;
	}
	
	private ShareList calcShareList()
	{
		ShareList shares = new ShareList();
		
		for(Object s : core.getConfig().keys())
		{
			String x = (String)s;
			
			if (x.startsWith("share.") && x.endsWith(".folder"))
			{
				int id = Integer.parseInt(x.split("\\.")[1]);
				String folder = core.getConfig().get(x);
				String grps = core.getConfig().get("share."+id+".visibility");
				List<String> groups = Str.isEmpty(grps) ? new ArrayList<String>() : Arrays.asList( grps.split(","));
				
				Share p = new Share(id, new File(folder).getAbsoluteFile(), groups);
				shares.put(p.getId(), p);
			}
		}
		shares.test(db);
		
		return shares;
	
	}
	
	public void start()
	{
		// notifyShares();
		
		
	}

	private void notifyShares()
	{
		for (Share x : shares.values())
		{
			core.getGui().pass(new ShareInfo(x.getId(), x.getFolder().getAbsolutePath(), x.getGroups()));
		}
/*		
		int i = 1;
	    while(true)
	    {
			String share = core.getConfig().get("share."+i+".folder");
			String groups = core.getConfig().get("share."+i+".visibility");
			if (share == null)
			{
			    break;
			}
			List<String> s = groups==null? new ArrayList<String>(): Arrays.asList(groups.split(","));
			core.getGui().pass(new ShareInfo(i, share, s));
			i++;
	    }
	    */
	}
	
	public boolean canSeeShare(Person p, int shareId)
	{
		Share s = shares.get(shareId);
		return canSeeShare(p, s);
	}

	public boolean canSeeShare(Person p, Share share)
	{
		if(share.getGroups().size() == 0) return true;
		
		List<String> res = ListUtils.intersect(share.getGroups(), p.groups);
		return res.size()>0;
	}

	public Set<Integer> visibleShares(Person p)
	{
		Set<Integer> res = new HashSet<Integer>();
		for (Share x : shares.values())
		{
			if(canSeeShare(p, x)) res.add(x.getId());
		}
	    return res;
	}
	public Set<Share> visibleShares2(Person p)
	{
		Set<Share> res = new HashSet<Share>();
		for (Share x : shares.values())
		{
			if(canSeeShare(p, x)) res.add(x);
		}
	    return res;
	}
	@Override
	public void message(Node node, Message msg) throws Exception
	{
	    if(msg instanceof BrowseSearch)
	    {
			String parent = ((BrowseSearch)msg).parent;
			
			Person p = core.getPeoples().get(node.getHash());
			
			DataPumpSource s = new FileListPump(parent, db, visibleShares2(p), shares);
			node.enqueue(s);
			
	    }
	    else if (msg instanceof BrowseReplyFolder)
	    {
	    	BrowseReplyFolder b = (BrowseReplyFolder)msg;
	    	
	    	GuiBrowseReplyFolder m = new GuiBrowseReplyFolder(node.getHash(), b.parent, b.name, b.hasSubdir);
	    	
	    	core.getGui().pass(m);
	    }
	    else if (msg instanceof BrowseReplyFile)
	    {
	    	BrowseReplyFile b = (BrowseReplyFile)msg;
	    	
	    	handleBrowseReplyFile(node, b);
	    }
	    else if (msg instanceof BrowseReplyFinished)
	    {
	    	BrowseReplyFinished b = (BrowseReplyFinished)msg;
	    	
	    	GuiBrowseReplyFinished m = new GuiBrowseReplyFinished(node.getHash(), b.parent);
	    	
	    	core.getGui().pass(m);
	    }
	    
		
	
	}


	private void handleBrowseReplyFile(Node node, BrowseReplyFile b) 
	{
		FileID fid;
		if(b.hash.equals(Hash.EMPTY))
		{
			// he don't have the hash. Generate a UserFileID to identify this file
			fid = new UserFileID(node.getHash(), b.parent+"/"+b.name, b.timeStamp, b.size);
		}
		else
		{
			fid = b.hash;
		}
		GuiBrowseReplyFile m = new GuiBrowseReplyFile(node.getHash(), b.parent, fid, b.name, b.size);
		
		core.getGui().pass(m);
	}

	@Override
	public void nodeConnected(Node node) throws Exception
	{

	}

	@Override
	public void nodeDisconnected(Node node) throws Exception
	{
	   
	}

	@Override
	public void taskTerminated(Task task, boolean interrupted) throws Exception
	{
	  
	}

	public FileInfo find(FileID fileId)
	{
		if(fileId instanceof Hash)
		{
			return db.getFileInfoByHash((Hash)fileId);
		}
		else if(fileId instanceof UserFileID)
		{
			UserFileID id = (UserFileID)fileId;
			if(!id.getUserHash().equals(core.getHash()))
			{
				System.err.println("RECEIVED USERFILEID of another user! "+id.getUserHash());
				return null;
			}
			File f = new File( shares.sharedToLocalPath(id.getFilePath()) );
			if(f.exists())
			{
				if( (f.length() == id.getSize()) && (f.lastModified() == id.getTimeStamp()) )
				{
					// see if we have the hash anyway (ie the file got hashed but requestor don't know)
					Hash hash = db.getHash(f, true);
										
					return new FileInfo(f.getAbsolutePath(), id.getTimeStamp(), id.getSize(), hash);
				}
			}
			
			return null;
			
		}
		else
		{
			throw new RuntimeException("Unknown fileID type");
		}
	}
	
	public void addHashedFile(Hash hash, File file)
	{
		db.put(hash, file);
	}
}
